import wx
import binascii
import cStringIO
from wx.lib.wordwrap import wordwrap
from operator import itemgetter
from os.path import join, split, isdir, basename
from time import localtime, strftime
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
from Base.list import ManagedList, ManagedTreeList
from Base.menu import Menu
from LMG.Utility.helpers import comma_format
from LMG.Utility.peerid import GetClientName
from LMG.Utility.constants import *
from LMG.GUI.Base.widgets import ProgressBar, Gauge, ScrolledHtmlDialog
from LMG.GUI.Base.ArtManager import ArtManager

class FileDropTarget(wx.FileDropTarget): 
    """
    Enable drag and drop for Transfer list.
    """
    
    def __init__(self):
        """
        Initialize the wsFileDropTarget Object.
        """
        wx.FileDropTarget.__init__(self) 
      
    def OnDropFiles(self, x, y, filenames):
        """
        Catch drag&drop action
        """
        for filename in filenames:
            if isdir(filename):
                utility.queue.addtorrents.ScanTorrentDir(filename)
            else:
                utility.queue.addtorrents.AddTorrentFromFile(filename, caller = "drag")
        return True
            
class TransferList(ManagedList):
    """
    Transter List class that contains the torrent list.
    """
    
    def __init__(self, parent):        
        # Init ManagedList
        style = wx.LC_REPORT|wx.LC_VRULES|wx.CLIP_CHILDREN|wx.LC_EDIT_LABELS#|wx.BORDER_STATIC
        prefix = 'column'
        minid = 0
        maxid = 18
        ManagedList.__init__(self, parent, style, prefix, minid, maxid)
        # Set drag and drop
        dragdroplist = FileDropTarget()
        self.SetDropTarget(dragdroplist)
        
        # Set ImageList
        self.il.Add(utility.makeBitmap('status', 'stopped.png'))      # Stopeed
        self.il.Add(utility.makeBitmap('status', 'working.png'))      # Working
        self.il.Add(utility.makeBitmap('status', 'completed.png'))    # Completed
        self.il.Add(utility.makeBitmap('status', 'seeding.png'))      # Seeding
        self.il.Add(utility.makeBitmap('status', 'superseeding.png')) # Super-seed
        self.il.Add(utility.makeBitmap('status', 'paused.png'))       # Paused
        self.il.Add(utility.makeBitmap('status', 'queue.png'))        # Queue
        self.il.Add(utility.makeBitmap('status', 'connecting.png'))   # Connecting
        self.il.Add(utility.makeBitmap('status', 'checking.png'))     # Checking
        self.il.Add(utility.makeBitmap('status', 'completedqueued.png')) # Completed/Queue
        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)

        # Set Find Dialog
        self.finddlg = None
        self.finddata = wx.FindReplaceData()
        self.finddata.SetFlags(wx.FR_DOWN)

        # Progress bars
        self.native = self == self.GetMainWindow()  
        self.progressBars = []
        self.progressBarType = None
        self.clearBars = False
        self._dragging = False
        self._dragCount = 0
        
        # Event Table
        self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)                  # Key Down
        self.Bind(wx.EVT_FIND, self.OnFind)                         # Find open
        self.Bind(wx.EVT_FIND_NEXT, self.OnFind)                    # Find next
        self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)              # Find close
        self.Bind(wx.EVT_PAINT, self.OnPaint)                       # Paint ProgressBars
        self.Bind(wx.EVT_LIST_COL_DRAGGING, self.OnPaint)           # Paint ProgressBars
        self.Bind(wx.EVT_LIST_COL_END_DRAG, self.OnPaint)           # Paint ProgressBars
        self.Bind(wx.EVT_SCROLL, self.OnPaint)                      # Paint ProgressBars
        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)                # Menu Event
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected)   # Item Selected  
        self.Bind(wx.EVT_LIST_ITEM_DESELECTED,self.onItemDeSelected)# Item Selected  
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onItemActivated) # Left Double click / Enter
        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.onBeginEdit)
        self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.onLabelChanged)
        
        if wx.Platform == "__WXMSW__":
            self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
        else:
            self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.onContextMenu)

    def InsertStringItem(self, *args, **kwargs):
        super(TransferList, self).InsertStringItem(*args, **kwargs)
        ArtManager.Get().MakeAlternateListItem(self, self.GetItemCount()-1)

    def onBeginEdit(self, event):
##        if event.GetColumn() != 0:
##            event.Veto()
##        else:
##            event.Allow()
        event.Allow()
        
    def onLabelChanged(self, event):
        torrent = utility.queue.getTorrent(index = event.GetIndex())
        if not event.IsEditCancelled():
            text = event.GetLabel() or torrent.getTitle("original")
            torrent.changeTitle(text)
        event.Veto()
        
    def onItemSelected(self, event):
        utility.window.details.addTorrent(utility.queue.getTorrent(index = event.GetIndex()))
        event.Skip()

    def onItemDeSelected(self, event):
        utility.window.details.removeTorrent(utility.queue.getTorrent(index = event.GetIndex()))
        event.Skip()

    def updateDetails(self):
        details = utility.window.details
        selected = self.getTorrentSelected()
        wasselected = details.torrents
        for torrent in set(selected + wasselected):
            if torrent in selected:
                details.addTorrent(torrent)
            else:
                details.removeTorrent(torrent)
                           
    def OnMouse(self, event):
        """
        Catch mouse events
        """

        # Left Down
        if event.LeftDown():
            (index, flags) = self.HitTest(event.GetPosition())

            # Show Details Tip Window
            if flags == wx.LIST_HITTEST_ONITEMICON:
                torrent = utility.queue.getTorrent(index = index)
                wx.TipWindow(self, torrent.getDetailsString(), 1000)
                self.updateSelected(unselect = range(0, self.GetItemCount()), select = [index])
                return
            
        # Middle Down
        elif event.MiddleDown():
            self.updateSelected(unselect = range(0, self.GetItemCount()), select = [self.HitTest(event.GetPosition())[0]])

        # Start Dragging
        elif event.Dragging() and not self._dragging and event.LeftIsDown():
            self._dragCount += 1
            if self._dragCount != 5:
                return
            self._dragging = True
            self._dragCount = 0
            self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))

        # End Dragging
        elif self._dragging and not event.Dragging():
            self._dragging = False
            self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

            index = self.HitTest(event.GetPosition())[0]
            
            if event.Leaving() and wx.Platform in ("__WXMSW__", "__WXGTK__"):
                data = wx.FileDataObject()
                for index in self.getSelected():
                    torrent = utility.torrents["all"][index]
                    if torrent.src:
                        data.AddFile(torrent.src)
                dropSource = wx.DropSource(self)
                dropSource.SetData(data)
                result = dropSource.DoDragDrop()
            else:
                self.onMoveItem(self.HitTest(event.GetPosition())[0])
           
        event.Skip()
        
    def onMoveItem(self, index):
        """
        Move item in list
        """
        # New location
        if not 0 <= index < self.GetItemCount():
            return
        
        # Old location
        selected = self.getSelected(firstitemonly = True)
        
        # Move!
        if selected:
            selected = selected[0]
            torrent = utility.torrents["all"].pop(selected)
            utility.torrents["all"].insert(index, torrent)
            utility.queue.updateListIndex(startindex = min(index, selected), endindex = max(index, selected))
            self.updateSelected(unselect = range(0, self.GetItemCount()), select = [index])
        
    def OnPaint(self, event=None):
        """
        Handles the EVT_PAINT event
        """
        if utility.config.Read("progress_bars", "int"):
            self._OnPaintBars()
        elif self.progressBars:
            [p.Destroy() for p in self.progressBars]
            del self.progressBars[:]
        if event:
            event.Skip()
        
    def _OnPaintBars(self):
        """
        Actual drawing of progress bars
        """
        # General list info
        rank = self.columns.getRankfromID(COL_PROGRESS)
        itemCount = self.GetItemCount()
        
        # No progress column or no items
        if rank == -1 or not itemCount:
            [p.Destroy() for p in self.progressBars]
            del self.progressBars[:]
            return False

        if self.clearBars:
            self.clearBars = False
            [p.Destroy() for p in self.progressBars]
            del self.progressBars[:]
            
        # Indexes
        topItem = self.GetTopItem() # top
        visibleIndexes = range(topItem, topItem + min(self.GetCountPerPage()+1, itemCount)) # to show
        
        # Make sure no extra bars
        while len(self.progressBars) > itemCount:
            progressBar = self.progressBars.pop()
            progressBar.Destroy()

        # Make sure enough bars
        while len(self.progressBars) < itemCount:
            progressBar = self._getProgressBar()
            self.progressBars.append(progressBar)

        # Update bars positions, size and value
        rect = self.GetItemRect(topItem)
        size = (self.GetColumnWidth(rank)-4, rect[3]-4)
        x = rect[0] + sum([self.GetColumnWidth(i) for i in range(0, rank)]) + 2
        if self.native:
            y = rect[1] + 2
        else:
            y = 2
        inc = rect[3]
        
        for row in range(itemCount):
            if row in visibleIndexes:
                bar = self.progressBars[row]
                if bar.GetPosition() != (x, y):
                    if wx.Platform != "__WXMSW__":
                        bar.Hide()
                    bar.SetPosition((x, y))
                bar.SetSize(size)
                bar.SetValue(utility.queue.getTorrent(index = row).getColumnValue(COL_PROGRESS))
                bar.Show()
                y += inc
            else:
                self.progressBars[row].Hide()

        return True
        
    def _getProgressBar(self):
        if self.progressBarType != None:
            return self.progressBarType(self.GetMainWindow())

        if utility.config.Read("progress_bars", "int") == 1:
            self.progressBarType = ProgressBar
        else:
            self.progressBarType = Gauge
            
        return self.progressBarType(self.GetMainWindow())

    def ChangeProgressBarType(self):
        self.progressBarType = None
        self.clearBars = True
        self.onUpdateColumns()

    def loadTranslation(self, update = False):
        """
        @override
        Sets columns text in utility.lang.base
        """
        # List Text
        utility.lang.base['column'+str(COL_TITLE)+'_text'] = _("Title")
        utility.lang.base['column'+str(COL_PROGRESS)+'_text'] = _("Progress")
        utility.lang.base['column'+str(COL_BTSTATUS)+'_text'] = _("Status")
        utility.lang.base['column'+str(COL_DLSPEED)+'_text'] = _("DL Speed")
        utility.lang.base['column'+str(COL_ULSPEED)+'_text'] = _("UL Speed")
        utility.lang.base['column'+str(COL_PRIO)+'_text'] = _("Priority")
        utility.lang.base['column'+str(COL_ETA)+'_text'] = _("ETA")
        utility.lang.base['column'+str(COL_SIZE)+'_text'] = _("Size")
        utility.lang.base['column'+str(COL_DONESIZE)+'_text'] = _("Completed")
        utility.lang.base['column'+str(COL_DLSIZE)+'_text'] = _("DL Size")
        utility.lang.base['column'+str(COL_ULSIZE)+'_text'] = _("UL Size")
        utility.lang.base['column'+str(COL_RATIO)+'_text'] = _("Ratio")
        utility.lang.base['column'+str(COL_MESSAGE)+'_text'] = _("Message")
        utility.lang.base['column'+str(COL_RESOURCES)+'_text'] = _("Seeds/Peers")
        utility.lang.base['column'+str(COL_TRACKER)+'_text'] = _("Tracker")
        utility.lang.base['column'+str(COL_ADDED_ON)+'_text'] = _("Added On")
        utility.lang.base['column'+str(COL_COMPLETED_ON)+'_text'] = _("Completed On")
        utility.lang.base['column'+str(COL_SEEDING_TIME)+'_text'] = _("Seeding time")

        if update:
            if hasattr(self, "columnpopup"):
                del self.columnpopup
            if hasattr(self, "popupmenu"):
                del self.popupmenu
            self.updateColumns()
            
    def onKeyDown(self, event):
        """
        Do thing when keys are pressed down.
        """
        keycode = event.GetKeyCode()
        if event.CmdDown():
            if keycode == ord('a') or keycode == ord('A'):
                # Select all files (CTRL-A)
                self.selectAll()
            elif keycode == ord('x') or keycode == ord('X'):
                # Invert file selection (CTRL-X)
                self.invertSelection()
            elif keycode == ord('f') or keycode == ord('F'):
                # Fast find dialog (CTRL-F)
                if self.finddlg != None:
                    return
                self.finddlg = wx.FindReplaceDialog(self,
                                                    self.finddata,
                                                    _("Find"),
                                                    wx.FR_NOWHOLEWORD)
                self.finddlg.Show()
            elif keycode == ord('s') or keycode == ord('S'):
                utility.actions[ACTION_SAVETORRENT].action()
            elif keycode == wx.WXK_UP:
                utility.actions[ACTION_MOVEUP].action()
            elif keycode == wx.WXK_DOWN:
                utility.actions[ACTION_MOVEDOWN].action()
            else:
                event.Skip()
                
        elif event.ShiftDown():
            if keycode == ord('r') or keycode == ord('R'):
                utility.actions[ACTION_RESUME].action()
            elif keycode == ord('s') or keycode == ord('S'):
                utility.actions[ACTION_STOP].action()
            elif keycode == ord('p') or keycode == ord('P'):
                utility.actions[ACTION_PAUSE].action()
            elif keycode == ord('q') or keycode == ord('Q'):
                utility.actions[ACTION_QUEUE].action()
            elif keycode == ord('h') or keycode == ord('H'):
                utility.actions[ACTION_HASHCHECK].action()
            elif keycode == ord('o') or keycode == ord('O'):
                utility.actions[ACTION_OPENDEST].action()
            elif keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
                utility.actions[ACTION_REMOVE].action()
            else:
                event.Skip()

        else:    
            event.Skip()
            
    def onContextMenu(self, event = None):
        """
        Show a context menu when right-clicking.
        """
        selected = self.getTorrentSelected()
        if not selected:
            return

        self.popupmenu = Menu('rightclickmenu')        
        self.PopupMenu(self.popupmenu)
    
    def onItemActivated(self, event):
        """
        Open the local settings window on a double-click
        """
        utility.actions[ACTION_TORRENTDETAILS].action()
#        self.EnsureVisible(event.GetIndex())
        event.Skip()
        
    def selectAll(self):
        """
        Select all items in the list.
        """
        self.updateSelected(select = range(0, self.GetItemCount()))

    def updateSelected(self, unselect = None, select = None):
        """
        (De)selects items
        """
        if unselect is not None:
            for index in unselect:
                if index != -1:
                    self.Select(index, False)
        if select is not None:
            for index in select:
                if index != -1:
                    self.Select(index, True)
        self.updateDetails()
        self.SetFocus()
            
    def getTorrentSelected(self, firstitemonly = False, reverse = False):
        """
        Get a listing of which torrents in the list are selected.
        """
        torrentselected = []
        for index in self.getSelected(firstitemonly, reverse):
            torrent = utility.queue.getTorrent(index = index)
            if torrent is not None:
                torrentselected.append(torrent)
        return torrentselected
    
    def onUpdateColumns(self, force = False):
        """
        Update display columns for inactive torrents.
        """
        for torrent in utility.torrents["all"]:
            torrent.updateColumns(force = force)

    def SortItems(self, sorter=None):
        """
        Sort the list
        """
        rank = self._col
        colid = self.columns.getIDfromRank(rank)
        sf = self._colSortFlag[rank]       
        utility.queue.sortList(colid, sf)
        self.OnPaint()

    def OnFind(self, event):
        """
        Find item in list
        """
        if self.GetItemCount() == 0:
            return
        
        searchitem = self.finddata.GetFindString()
        if searchitem.strip() == "":
            return
    
        casesen = bool((self.finddata.GetFlags() & wx.FR_NOMATCHCASE))
        if not casesen:
            searchitem = searchitem.lower()

        latestindex = self.GetFirstSelected()

        backward = not (self.finddata.GetFlags() & wx.FR_DOWN)
        if backward:
            row = latestindex - 1
        else:
            row = latestindex + 1
                
        counter = 0
        while counter < self.GetItemCount():
            # Wrap around
            if backward and row < 0:
                row = (self.GetItemCount()-1)
            elif not backward and row >= self.GetItemCount():
                row = 0
                
            # Torrent name
            torrent = utility.queue.getTorrent(index = row)
            name = torrent.getTitle(kind = "current")
            if not casesen:
                name = name.lower()
                
            # Have a match
            if name.find(searchitem) > -1:
                # Select just what we just found
                for index in xrange(self.GetItemCount()):
                    self.Select(index, index==row)
                    self.EnsureVisible(row)
                if self.finddlg:
                    self.finddlg.SetFocus()
                return
            # No Match
            if backward:
                row -= 1
            else:
                row += 1
            counter += 1

        if self.finddlg:
            self.finddlg.Destroy()
            self.finddlg = None

        dialog = wx.MessageDialog(self,
                                  _('No match could be found'),
                                  _('Found nothing'),
                                  wx.OK | wx.ICON_INFORMATION)
        dialog.ShowModal()
        dialog.Destroy()


    def OnFindClose(self, event):
        """
        Close find dialog
        """
        event.GetDialog().Destroy()
        self.finddlg = None
        
class GeneralInfoList(wx.ListCtrl, ListCtrlAutoWidthMixin):
    def __init__(self, parent):
        style = wx.LC_REPORT|wx.CLIP_CHILDREN|wx.BORDER_NONE|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER 
        wx.ListCtrl.__init__(self, parent, -1, style = style)
        ListCtrlAutoWidthMixin.__init__(self)
        
        self.torrent = None
        
        # Insert columns
        self.InsertColumn(0, _("Item"))
        self.InsertColumn(1, _("Value"))
        self.SetColumnWidth(0, 120)

        self.min = 0
        self.max = 13
        
        # Items
        self.name           = self.InsertStringItem(0, _("Name"))
        self.destination    = self.InsertStringItem(1, _("Destination"))
        self.size           = self.InsertStringItem(2, _("Size"))
        self.pieces         = self.InsertStringItem(3, _("Pieces"))
        self.infoHash       = self.InsertStringItem(4, _("Info Hash"))
        self.magnet         = self.InsertStringItem(5, _("Magnet URI"))
        self.InsertStringItem(6, "")
        self.comment        = self.InsertStringItem(7, _("Comment"))
        self.creationDate   = self.InsertStringItem(8, _("Creation Date"))
        self.createdBy      = self.InsertStringItem(9, _("Created by"))
        self.InsertStringItem(10, "")
        self.encoding       = self.InsertStringItem(11, _("Encoding"))
        self.private        = self.InsertStringItem(12, _("Private"))

        self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.onRightClick)
        self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)

    def onKeyDown(self, event):
        """
        Do things when keys are pressed
        """
        keycode = event.GetKeyCode()
        if event.CmdDown() and keycode == ord('c') or keycode == ord('C'):
            self.onCopySingle()
        event.Skip()

    def setValue(self, index, value):
        self.SetStringItem(index, 1, value)
        
    def clearValues(self):
        self.setValue(self.name, "")
        self.setValue(self.destination, "")
        self.setValue(self.size, "")
        self.setValue(self.pieces, "")
        self.setValue(self.encoding, "")
        self.setValue(self.private, "")
        self.setValue(self.creationDate, "")
        self.setValue(self.createdBy, "")
        self.setValue(self.infoHash, "")
        self.setValue(self.magnet, "")
        self.setValue(self.comment, "")

    def getColumnText(self, column):
        if self.torrent is None:
            return ""
        
        if column == self.name:
            text = self.torrent.getTitle(kind = "current")
        elif column == self.destination:
            text = self.torrent.files.getProcDest(pathonly = True, checkexists = False)
        elif column == self.size:
            totalsize = self.torrent.files.getSize()
            text = _("%s (%s bytes)") % (utility.size_format(totalsize), comma_format(totalsize))
        elif column == self.pieces:
            piece_length = self.torrent.info['piece length']
            pieces = int((self.torrent.files.getSize() + piece_length - 1)/piece_length)
            if pieces > 1:
                text = _("%i x %s") % (pieces, utility.size_format(piece_length))
            else:
                text = str(pieces)
        elif column == self.encoding:
            text = _('N/A')
            if 'encoding' in self.torrent.metainfo and self.torrent.metainfo['encoding'].strip():
                text = self.torrent.metainfo['encoding']
        elif column == self.private:
            text = _('No')
            if self.torrent.private:
                text = _('Yes')
        elif column == self.creationDate:
            try:
                text = strftime('%x %X', localtime(self.torrent.metainfo['creation date']))
            except:
                try:
                    text = str(self.torrent.metainfo['creation date'])
                except:
                    text = _('<cannot read date>')
        elif column == self.createdBy:
            text = _('N/A')
            if 'created by' in self.torrent.metainfo and self.torrent.metainfo['created by'].strip():
                text = self.torrent.metainfo['created by']
        elif column == self.infoHash:
            text = self.torrent.infohash
        elif column == self.magnet:
            text = self.torrent.getMagnetLink()
        elif column == self.comment:
            text = _('N/A')
            if 'comment' in self.torrent.metainfo and self.torrent.metainfo['comment'].strip():
                text = self.torrent.metainfo['comment']
        else:
            text = ""
        return text
    
    def loadValues(self, torrent = None):
        self.torrent = torrent
        if torrent is None:
            self.clearValues()
            return
        for i in range(self.min, self.max):
            self.setValue(i, self.getColumnText(i).replace('\r\n', '\n'))

    def onRightClick(self, event):
        if self.torrent is None:
            return
        
        menu = Menu()
        if event.GetIndex() == self.destination:
            utility.makePopup(menu, self.onOpenDest, _('Open'))
        elif event.GetIndex() == self.comment:
            utility.makePopup(menu, self.onViewContent, _('View'))
        utility.makePopup(menu, self.onCopySingle, _('Copy'), ID = wx.ID_COPY)
        self.PopupMenu(menu, event.GetPosition())

    def onViewContent(self, event):
        try:
            content = self.torrent.metainfo['azureus_properties']['Content']
        except:
            content = None

        if not content:
            dlg = ScrolledHtmlDialog(self, self.getColumnText(self.comment), _("Torrent Comment"))
        else:
            dlg = wx.Dialog(self, -1, title = _("Torrent Comment"))
            outsizer = wx.BoxSizer(wx.VERTICAL)
            sizer = wx.FlexGridSizer(cols = 2, hgap = 10, vgap = 10)
            
            if 'Title' in content:
                    sizer.Add(wx.StaticText(dlg, -1, _("Title:")), 0)
                    sizer.Add(wx.StaticText(dlg, -1, wordwrap(content['Title'], 350, wx.ClientDC(dlg))), 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5)
            if 'Description' in content:
                    sizer.Add(wx.StaticText(dlg, -1, _("Description:")), 0)
                    sizer.Add(wx.StaticText(dlg, -1, wordwrap(content['Description'], 350, wx.ClientDC(dlg))), 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5)
            if 'Publisher' in content:
                    sizer.Add(wx.StaticText(dlg, -1, _("Publisher:")), 0)
                    sizer.Add(wx.StaticText(dlg, -1, wordwrap(content['Publisher'], 350, wx.ClientDC(dlg))), 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5)                
            if 'Content Type' in content:
                    sizer.Add(wx.StaticText(dlg, -1, _("Content Type:")), 0)
                    sizer.Add(wx.StaticText(dlg, -1, wordwrap(content['Content Type'], 350, wx.ClientDC(dlg))), 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5)                
            if 'Quality' in content:
                    sizer.Add(wx.StaticText(dlg, -1, _("Quality:")), 0)
                    sizer.Add(wx.StaticText(dlg, -1, wordwrap(content['Quality'], 350, wx.ClientDC(dlg))), 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5)                                
            if 'comment' in self.torrent.metainfo and self.torrent.metainfo['comment'].strip():
                    sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0)
                    html = wx.html.HtmlWindow(dlg, -1, size = (250, 100))
                    html.SetPage(self.torrent.metainfo['comment'])
                    html.SetBackgroundColour(wx.NullColour)
                    sizer.Add(html, 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5)
            if 'Thumbnail' in content:
                try:
                    stream = cStringIO.StringIO(content['Thumbnail'])
                    image = wx.ImageFromStream(stream)
                    bmp = wx.BitmapFromImage(image)
                    sizer.Add(wx.StaticText(dlg, -1, _("Thumbnail:")), 0)
                    sizer.Add(wx.StaticBitmap(dlg, -1, bmp), 1, wx.ALL, 5)
                except:
                    pass
                else:
                    stream.close()
            outsizer.Add(sizer, 1, wx.EXPAND|wx.ALL, 3)
            outsizer.Add(dlg.CreateSeparatedButtonSizer(wx.OK), 0, wx.EXPAND|wx.GROW, 0)
            dlg.SetSizerAndFit(outsizer)
        
        dlg.ShowModal()
        dlg.Destroy()
        
    def onCopySingle(self, event = None):
        # Copy the text to the clipboard
        if wx.TheClipboard.Open():
            data = wx.TextDataObject(self.getColumnText(self.GetFirstSelected()))
            wx.TheClipboard.SetData(data)
            wx.TheClipboard.Close()

    def onOpenDest(self, event):
        if self.torrent:
            self.torrent.files.onOpenDest()
        
class PeersList(ManagedList):
    def __init__(self, parent):        
        style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_VRULES|wx.CLIP_CHILDREN|wx.BORDER_STATIC
        prefix = 'spew'
        minid = 0
        maxid = 15 
        ManagedList.__init__(self, parent, style, prefix, minid, maxid)
        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)

        self.totalAttr = wx.ListItemAttr()
        self.totalAttr.SetBackgroundColour("yellow")
        self.evenAttr = wx.ListItemAttr()
        ArtManager.Get().MakeAlternateList(self)
        
        self.infoList = []

        # Column Sorter
        self.sort_colid = utility.config.Read(self.prefix + '_sort_colid', "int")
        self.sort_flag  = utility.config.Read(self.prefix + '_sort_flag', "boolean")
        self._col = self.columns.getRankfromID(self.sort_colid)
        if self._col >= 0:
            self._colSortFlag[self._col] = self.sort_flag
            sortImages = self.GetSortImages()
            img = sortImages[self._colSortFlag[self._col]]
            self.SetColumnImage(self._col, img)

        # Columns
        self.map = ['id', 'ip', 'optimistic', 'direction', 'uprate',
                    'uinterested', 'uchoked', 'downrate', 'dinterested',
                    'dchoked', 'snubbed', 'utotal', 'dtotal', 'completed', 'speed']

    def MakeAlternateList(self, c):
        self.evenAttr.SetBackgroundColour(c)
        
    def SortItems(self, sorter=None):
        """
        Sort the list
        """
        rank = self._col
        self.sort_colid = self.columns.getIDfromRank(rank)
        self.sort_flag = self._colSortFlag[rank]
        utility.config.Write(self.prefix + '_sort_colid', self.sort_colid, "int")
        utility.config.Write(self.prefix + '_sort_flag', self.sort_flag, "boolean")
                
    def onUpdateColumns(self, force = False):
        """
        Update display.
        """
        # Set length
        newLength = len(self.infoList)
        oldLength = len(self.oldInfoList)
        if oldLength != newLength:
            self.SetItemCount(newLength)
        if not newLength:
            return
       

        # Get update range
        itemFrom = self.GetTopItem()
        itemTo   = self.GetTopItem() + self.GetCountPerPage() + 1
        itemTo   = min(itemTo, self.GetItemCount())
        
        # Update what needed
        for item in xrange(itemFrom, itemTo):
            if oldLength <= item or self.infoList[item] != self.oldInfoList[item]:
                self.RefreshItem(item)
        
    def updateSpew(self, infoList):
        total = infoList.pop(0)
        if 0 <= self.sort_colid < self.maxid:
            infoList.sort(key=itemgetter(self.map[self.sort_colid]),
                          reverse = self.sort_flag)
        infoList.insert(0, total)
        
        self.oldInfoList = self.infoList
        self.infoList = infoList
        self.onUpdateColumns()

    def clearSpew(self):
        self.oldInfoList = self.infoList = []
        if self.GetItemCount():
            self.RefreshItems(0, self.GetItemCount()-1)
            self.SetItemCount(0)
        
    def OnGetItemText(self, item, col):
        spew = self.getSpewByRank(col)
        if spew in ('uprate', 'downrate'):
            return utility.speed_format(int(self.infoList[item][spew]), truncate = 0, stopearly = "KB")
        if spew in ('utotal', 'dtotal'):
            return utility.size_format(float(self.infoList[item][spew]))
        if spew == 'id':
            if item == 0:
                return _("Total/Average")
            return GetClientName(self.infoList[item][spew])        
        if item == 0:
            return self.infoList[item].get(spew, "")
        if spew == 'completed':
            return '%.1f%%' % (float(int(self.infoList[item][spew]*1000))/10)
        return self.infoList[item][spew]
    
    def OnGetItemAttr(self, item):
        if item == 0:
            return self.totalAttr
        elif item % 2 == 0:
            return self.evenAttr
        return None
        
    def getSpewByRank(self, rank):
        return self.map[self.columns.getIDfromRank(rank)]
    
    def loadTranslation(self, update = False):
        """
        @override
        Sets columns text in utility.lang.base
        """
        # List Text
        utility.lang.base['spew0_text'] = _("ID")
        utility.lang.base['spew1_text'] = _("IP")
        utility.lang.base['spew2_text'] = _("Optimistic")
        utility.lang.base['spew3_text'] = _("Direction")
        utility.lang.base['spew4_text'] = _("UL Rate")
        utility.lang.base['spew5_text'] = _("Interesting")
        utility.lang.base['spew6_text'] = _("Choking")
        utility.lang.base['spew7_text'] = _("DL Rate")
        utility.lang.base['spew8_text'] = _("Interested")
        utility.lang.base['spew9_text'] = _("Choked")
        utility.lang.base['spew10_text'] = _("Snubbed")
        utility.lang.base['spew11_text'] = _("Uploaded")
        utility.lang.base['spew12_text'] = _("Downloaded")
        utility.lang.base['spew13_text'] = _("Completed")
        utility.lang.base['spew14_text'] = _("Speed")

        if update:
            self.updateColumns()

class FileInfoList(ManagedTreeList):
    """
    Used by multi-file torrents to display information about
    each of the files.
    """
    def __init__(self, parent):        
        self.parent = parent
        
        style = wx.CLIP_CHILDREN|wx.LC_REPORT|wx.BORDER_STATIC
        style = style|wx.TR_DEFAULT_STYLE|wx.TR_MULTIPLE|wx.TR_EXTENDED|wx.TR_FULL_ROW_HIGHLIGHT
        
        prefix = 'fileinfo'
        minid = 0
        maxid = 7
        rightalign = [FILEINFO_SIZE, FILEINFO_PROGRESS]
        centeralign = []
        maincolumn = FILEINFO_FILENAME

        ManagedTreeList.__init__(self, parent, style, prefix, minid, maxid, maincolumn, rightalign, centeralign)        

        self.priorityIDs = [wx.NewId(), wx.NewId(), wx.NewId(), wx.NewId()]
        self.prioritycolors = [ wx.Colour(160, 160, 160), 
                                wx.Colour(255, 64, 0), 
                                wx.Colour(0, 0, 0), 
                                wx.Colour(64, 64, 255) ]

        self.GetMainWindow().Bind(wx.EVT_RIGHT_UP, self.onItemSelected)
        self.GetMainWindow().Bind(wx.EVT_LEFT_DCLICK, self.onOpenFileDest)
        self.GetMainWindow().Bind(wx.EVT_KEY_DOWN, self.onKeyDown)

    def onKeyDown(self, event):
        """
        Do things when keys are pressed
        """
        keycode = event.GetKeyCode()
        if event.CmdDown():
            if keycode == ord('a') or keycode == ord('A'):
                # Select all files (CTRL-A)
                self.selectAll()
            elif keycode == ord('x') or keycode == ord('X'):
                # Invert file selection (CTRL-X)
                self.invertSelection()
            elif keycode == ord('c') or keycode == ord('C'):
                self.onCopyFilename()
        elif keycode == 399:
            # Open right-click menu (windows menu key)
            self.onItemSelected()

        event.Skip()
        
    def getRealSelected(self, firstitemonly = False):
        """
        Convert a list of which treeitems are selected
        into a listing of which files are selected
        """
        if not self.parent.parent.getTorrent():
            return
        treeitems = self.getSelected(firstitemonly = firstitemonly)
        
        realselected = [index for index, treeid in self.parent.filemap.items() if treeid in treeitems]
               
        return realselected

    def onOpenFileDest(self, event = None):
        """
        Open the destination file for a torrent
        """
        torrent = self.parent.parent.getTorrent()
        if not torrent:
            return
        for index in self.getRealSelected(firstitemonly = True):
            torrent.files.onOpenFileDest(index = index)
        
    def onOpenDest(self, event = None):
        """
        Open the destination for a torrent
        """
        torrent = self.parent.parent.getTorrent()
        if not torrent:
            return
        for index in self.getRealSelected(firstitemonly = True):
            torrent.files.onOpenFileDest(index = index, pathonly = True)

    def onCopyFilename(self, event = None, pathonly = False):
        """
        Copy the filenames for one or more files
        """
        torrent = self.parent.parent.getTorrent()
        if not torrent:
            return
        # Get the filenames
        text = ""
        count = 0
        for item in self.GetSelections():
            if count > 0:
                text += "\n"
            rank = self.columns.getRankfromID(FILEINFO_FILENAME)
            filename = self.GetItemText(item, rank)
            text += filename
            count += 1
                                    
        # Copy the text to the clipboard
        if wx.TheClipboard.Open():
            data = wx.TextDataObject(text)
            wx.TheClipboard.SetData(data)
            wx.TheClipboard.Close()

    def onCopyPath(self, event = None):
        """
        Copy just the paths for files
        """
        torrent = self.parent.parent.getTorrent()
        if not torrent:
            return

        # Get the filenames
        text = ""
        count = 0
        for index in self.getRealSelected():
            if count > 0:
                text += "\n"
            filename = torrent.files.getSingleFileDest(index, True, checkexists = False)                
            text += filename
            count += 1
        
        # Copy the text to the clipboard
        if wx.TheClipboard.Open():
            data = wx.TextDataObject(text)
            wx.TheClipboard.SetData(data)
            wx.TheClipboard.Close()

    def makePriorityMenu(self):
        torrent = self.parent.parent.getTorrent()
        if not torrent:
            return
        s = self.getRealSelected()
        if not s:
            return None

        priorities = torrent.files.filepriorities
        oldstate = priorities[s[0]]
        kind = wx.ITEM_RADIO
        for i in s[1:]:
            if priorities[i] != oldstate:
                oldstate = None
                kind = wx.ITEM_NORMAL
                break
            
        menu = Menu()
        menu.Append(self.priorityIDs[1], _('High'), kind=kind)
        menu.Append(self.priorityIDs[2], _('Normal'), kind=kind)
        menu.Append(self.priorityIDs[3], _('Low'), kind=kind)
        menu.Append(self.priorityIDs[0], _('Disable'), kind=kind)
        if oldstate is not None:
            menu.Check(self.priorityIDs[oldstate+1], True)

        def onSelection(event, self = self, s = s):
            p = event.GetId()
            torrent = self.parent.parent.getTorrent()
            priorities = torrent.files.filepriorities
            for i in xrange(len(self.priorityIDs)):
                if p == self.priorityIDs[i]:
                    for ss in s:
                        priorities[ss] = i - 1
                        
                        treeid = self.parent.filemap[ss]
                        self.SetItemTextColour(treeid, self.prioritycolors[i])

                    torrent.files.setFilePriorities(priorities)
                    self.Refresh()
                    break
            
        for index in self.priorityIDs:
            self.Bind(wx.EVT_MENU, onSelection, id = index)
        
        return menu

    def onItemSelected(self, event = None):
        torrent = self.parent.parent.getTorrent()
        if not torrent:
            return
        s = self.getRealSelected()

        if not s:   # just in case
            return

        menu = Menu()
        
        utility.makePopup(menu, self.onCopyFilename, _('Copy Filename'))
        utility.makePopup(menu, self.onCopyPath, _('Copy Path'))
        utility.makePopup(menu, self.onOpenDest, _('Open Destination'))
        utility.makePopup(menu, self.onOpenFileDest, 'Open File')

        # Add the priority submenu if this is a multi-file torrent
        if not torrent.files.isFile():
            prioritymenu = self.makePriorityMenu()
            if prioritymenu is not None:
                menu.AppendMenu(-1, _('Priority'), prioritymenu)

        # Popup the menu.  If an item is selected then its handler
        # will be called before PopupMenu returns.
        if event is None:
            position = (-1, -1)
        else:
            # use the cursor position (mouse event)
            position = event.GetPosition()

        self.PopupMenu(menu, position)

    def onUpdateColumns(self, columnlist = None, force = False):
        torrent = self.parent.parent.getTorrent()
        if not torrent:
            return
        priorities = torrent.files.filepriorities

        info = torrent.info
            
        if torrent.files.isFile():
            filesinfo = [info]
        else:
            filesinfo = info['files']
            
        try:
            for index in range(len(filesinfo)):
                tempfile = filesinfo[index]
                
                treeid = self.parent.filemap[index]
                for colid, rank in self.columns.active:
                    if columnlist is None or colid in columnlist:
                        self.setString(treeid, rank, self.getFileColumnText(colid, tempfile, index))
    
                p = priorities[index]
                
                self.SetItemTextColour(treeid, self.prioritycolors[p+1])
            #ArtManager.Get().MakeAlternateList(self)
        except wx.PyDeadObjectError:
            pass

    def getFileColumnText(self, colid, tempfile, index = 0):
        torrent = self.parent.parent.getTorrent()
        if torrent is None:
            return ""
        
        text = None
        
        if colid == FILEINFO_FILENAME:
            if torrent.files.isFile():
                text = split(torrent.files.getProcDest(pathonly = False, checkexists = False))[1]
            else:
                if tempfile['path']:
                    text = tempfile['path'][-1]
        elif colid == FILEINFO_SIZE:
            if torrent.files.isFile() or torrent.files.filepriorities[index] != -1:
                text = comma_format(tempfile['length']) + ' ' + _('Byte')
        elif colid == FILEINFO_PROGRESS:
            if torrent.files.isFile():
                if not torrent.status.isCheckingOrAllocating():
                    if torrent.status.completed:
                        text = _('Done')
                    else:
                        text = torrent.getColumnText(COL_PROGRESS, force = True)
            else:
                progress = torrent.files.fileprogress[index]
                if progress == -1:
                    # Don't download (or hasn't started)
                    text = ''
                elif progress > 100:
                    # Complete
                    text = _('Done')
                else:
                    # Downloading
                    text = '%d%%' % progress
        elif colid == FILEINFO_MD5:
            if 'md5sum' in tempfile:
                text = str(tempfile['md5sum'])
        elif colid == FILEINFO_CRC32:
            if 'crc32' in tempfile:
                text = str(tempfile['crc32'])
        elif colid == FILEINFO_SHA1:
            if 'sha1' in tempfile:
                text = binascii.b2a_hex(tempfile['sha1'])
        elif colid == FILEINFO_ED2K:
            if 'ed2k' in tempfile:
                text = binascii.b2a_hex(tempfile['ed2k'])
            
        if text is None:
            text = ""

        return text

    def loadTranslation(self, update = False):
        """
        @override
        Sets columns text in utility.lang.base
        """
        # List Text
        utility.lang.base['fileinfo'+str(FILEINFO_FILENAME)+'_text'] = _("Filename")
        utility.lang.base['fileinfo'+str(FILEINFO_SIZE)+'_text'] = _("Size")
        utility.lang.base['fileinfo'+str(FILEINFO_PROGRESS)+'_text'] = _("Progress")
        utility.lang.base['fileinfo'+str(FILEINFO_MD5)+'_text'] = _("MD5")
        utility.lang.base['fileinfo'+str(FILEINFO_CRC32)+'_text'] = _("CRC32")
        utility.lang.base['fileinfo'+str(FILEINFO_SHA1)+'_text'] = _("SHA1")
        utility.lang.base['fileinfo'+str(FILEINFO_ED2K)+'_text'] = _("ED2K")

        if update:
            self.updateColumns()
